import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;

/**
 * CAN.java
 * This does not represent a real CAN. It is used to initialize all the nodes in the system.
 * And Nodes can use this class to find their six neighbors.
 * @author Praneeth Pulusani
 *
 */
public class CAN {
	//Class variables
	private int dimX;
	private int dimY;
	private int dimZ;
	private HashMap<String, Node> nodeMap;
	
	/**
	 * CAN - Initializes the CAN system with the given dimension
	 * @param dX - width of can
	 * @param dY - height of can
	 * @param dZ - depth of can
	 */
	public CAN(int dX,int dY, int dZ){
		this.dimX=dX;
		this.dimY=dY;
		this.dimZ=dZ;
		nodeMap = new HashMap<String, Node>();
		for(double x = 0.5; x<dX;x = x+1){
			for (double y =0.5; y<dY;y=y+1 ){
				for (double z =0.5; z<dZ;z=z+1){
					nodeMap.put(""+x+y+z, new Node(x,y,z,this));
				}
			}
		}
		
	}
	/**
	 * getWidth
	 * @return the width of this CAN system
	 */
	public int getWidth(){
		return this.dimX;
	}
	/**
	 * getHeight
	 * @return the height of this CAN system
	 */
	public int getHeight(){
		return this.dimY;
	}
	/**
	 * getDepth
	 * @return the depth of this CAN system
	 */
	public int getDepth(){
		return this.dimZ;
	}
	/**
	 * getRandomStartNode - picks a random node from the system and returns it
	 * All nodes are bootstrap nodes
	 * @return a random bootstrap node
	 */
	public Node getRandomStartNode(){
		Object[] nodes =  nodeMap.values().toArray();
		return (Node) nodes[(new Random()).nextInt(nodes.length)];
		
	}
	/**
	 * getNode
	 * returns the desired node
	 * @param nodeid
	 * @return node from nodemap that has given nodeid
	 */
	public Node getNode(String nodeid){
		return nodeMap.get(nodeid);
	}
	/**
	 * getNeighbors
	 * A node will call its CAN object passing in itself.
	 * CAN will calculate the nodes 6 neighbors and returns
	 * references to them from the nodemap
	 * @param node - node whose 6 neighbors to find
	 * @return - 6 neighbors of the node
	 */
	public ArrayList<Node> getNeighbors(Node node) {
		
		//6 neighbors
		ArrayList<Node> neighbors = new ArrayList<Node>();
		//get lower neighbor from x dimension
		double lowerX =node.getX()-1;
		//if the node is out of bounds, wrap around
		if(lowerX<0){
			lowerX=dimX-0.5;
		}
		neighbors.add(nodeMap.get(""+lowerX+node.getY()+node.getZ()));
		
		//get upper neighbor from x dimension
		double upperX = node.getX()+1;
		//if the node is out of bounds, wrap around
		if(upperX>dimX-0.5){
			upperX=0.5;
		}
		neighbors.add(nodeMap.get(""+upperX+node.getY()+(node.getZ())));
		
		//get lower neighbor from y dimension
		double lowerY =node.getY()-1;
		//if the node is out of bounds, wrap around
		if(lowerY<0){
			lowerY=dimY-0.5;
		}
		neighbors.add(nodeMap.get(""+node.getX()+lowerY+node.getZ()));
		//get upper neighbor from y dimension
		double upperY = node.getY()+1;
		//if the node is out of bounds, wrap around
		if(upperY>dimY-0.5){
			upperY=0.5;
		}
		neighbors.add(nodeMap.get(""+node.getX()+upperY+node.getZ()));
		//get lower neighbor from z dimension
		double lowerZ =node.getZ()-1;
		//if the node is out of bounds, wrap around
		if(lowerZ<0){
			lowerZ=dimZ-0.5;
		}
		neighbors.add(nodeMap.get(""+node.getX()+node.getY()+lowerZ));
		//get upper neighbor from z dimension
		double upperZ = node.getZ()+1;
		//if the node is out of bounds, wrap around
		if(upperZ>dimZ-0.5){
			upperZ=0.5;
		}
		neighbors.add(nodeMap.get(""+node.getX()+node.getY()+upperZ));
		return neighbors;
	}
	
	

}
